﻿using System;
using Microsoft.Xna.Framework;
using RainEngine.SceneManagement;
using Microsoft.Xna.Framework.Graphics;

namespace RainEngine
{
    /// <summary>
    /// 辅助类
    /// </summary>
    public static class Utility
    {
        /// <summary>
        /// 创建一个旋转四元数，它绕着eyeDirection指向targetDirection的方向旋转。
        /// </summary>
        /// <param name="eyeDirection">视线方向</param>
        /// <param name="targetDirection">目标方向</param>
        /// <param name="rotation">旋转</param>
        public static void CreateLookAtRotation(ref Vector3 eyeDirection, ref Vector3 targetDirection, out Quaternion rotation)
        {
            if (targetDirection == Vector3.Zero)
                throw new InvalidOperationException("Target can not be Vector3.Zero");

            eyeDirection.Normalize();
            targetDirection.Normalize();
            Vector3 vNormal = Vector3.Cross(eyeDirection, targetDirection);
            if (vNormal != Vector3.Zero)
                vNormal.Normalize();

            float angle = Vector3.Dot(eyeDirection, targetDirection);
            angle = (float)Math.Acos(angle);
            float halfAngle = angle * 0.5f;  //   half angle
            float halfSine = (float)Math.Sin(halfAngle);

            rotation = new Quaternion(halfSine * vNormal.X,
                                      halfSine * vNormal.Y,
                                      halfSine * vNormal.Z,
                                     (float)Math.Cos(halfSine));
        }
        
        /// <summary>
        /// 创建一个四元数，它绕着eyeDirection指向targetDirection的方向旋转。
        /// </summary>
        /// <param name="eyeDirection"></param>
        /// <param name="targetDirection"></param>
        /// <returns></returns>
        public static Quaternion CreateLookAtRotation(ref Vector3 eyeDirection, ref Vector3 targetDirection)
        {
            Quaternion tmp;
            CreateLookAtRotation(ref eyeDirection, ref targetDirection, out tmp);
            return tmp;
        }
        
        /// <summary>
        /// 将一个四元数转换为一个Vector3，这个Vector3包含四元数旋转的Euler角。
        /// </summary>
        /// <param name="input">要转换的四元数</param>
        /// <returns></returns>
        public static Vector3 QuaternionToEulerAngles(ref Quaternion input)
        {
            Vector3 tmp = new Vector3();
            QuaternionToEulerAngles(ref input, ref tmp);
            return tmp;
        }
        
        /// <summary>
        /// 将一个四元数转换为一个Vector3，这个Vector3包含Euler角，角度值等于绕轴的旋转。
        /// </summary>
        /// <param name="input">要转换的四元数</param>
        /// <param name="eulerAngles">欧拉角</param>
        public static void QuaternionToEulerAngles(ref Quaternion input, ref Vector3 eulerAngles)
        {
            double pitch, yaw, roll = 0.0f;

            if (input.W > 1)
                input.Normalize();

            float x = input.X;
            float y = input.Y;
            float z = input.Z;
            float w = input.W;

            double sqx = x * x;
            double sqy = y * y;
            double sqz = z * z;
            double sqw = w * w;
            double unit = sqx + sqy + sqz + sqw;

            double test = x * y + z * w;
            if (test > 0.499f * unit)
            { // singularity at north pole
                yaw = 2 * Math.Atan2(x, w);
                roll = MathHelper.PiOver2;
                pitch = 0;
            }
            else if (test < -0.499f * unit)
            { // singularity at south pole
                yaw = -2 * Math.Atan2(x, w);
                roll = -MathHelper.PiOver2;
                pitch = 0;
            }
            else
            {

                yaw = Math.Atan2(2 * y * w - 2 * x * z, sqx - sqy - sqz + sqw);
                roll = Math.Asin(2 * test / unit);
                pitch = Math.Atan2(2 * x * w - 2 * y * z, -sqx + sqy - sqz + sqw);
            }

            eulerAngles.X = MathHelper.ToDegrees((float)pitch);
            eulerAngles.Y = MathHelper.ToDegrees((float)yaw);
            eulerAngles.Z = MathHelper.ToDegrees((float)roll);
        }

        /// <summary>
        /// 从内容管道或文件加载纹理。
        /// 首先检查文件是否是预编译的.xnb文件并加载，
        /// 否则从content目录搜索文件名称并加载，否则将文件看做绝对路径并加载。
        /// 如果没有找到对应的纹理则抛出一个FileNotFound错误。
        /// </summary>
        /// <param name="engine">引擎</param>
        /// <param name="assetOrFileName">纹理名称</param>
        /// <returns>纹理</returns>
        public static Texture2D LoadTexture(RainGE engine, string assetOrFileName)
        {
            Texture2D texture;
            string name = System.IO.Path.Combine(engine.Content.RootDirectory, assetOrFileName);
            name = name.Replace(".xnb", "");
            //if (System.IO.File.Exists(name + ".xnb"))
            //{
            texture = engine.Content.Load<Texture2D>(assetOrFileName);
            //}
            //else
            //{
                //name = System.IO.Path.Combine("11", "Content\\" + assetOrFileName);
                //if (System.IO.File.Exists(name + ".xnb"))
                //{
                    //texture = engine.Content.Load<Texture2D>(name);
                //}
                //else
                //{
                    //if (System.IO.File.Exists(name))
                        //texture = Texture2D.FromStream (engine.GraphicsDevice, name);
                    //else
                        //texture = Texture2D.FromFile(engine.GraphicsDevice, assetOrFileName);
                //}
            //}
            //if (texture != null)
                //texture.Name = assetOrFileName;
            return texture;
            //System.IO.Stream stream = TitleContainer.OpenStream("ship.dds");
            //System.IO.StreamReader sreader = new System.IO.StreamReader(stream);

            //return Texture2D.FromStream(engine.GraphicsDevice, stream);
        }
        

        /// <summary>
        /// 随机数生成器
        /// </summary>
        private static Random random = new Random();
        
        public static Random Random
        {
            get { return random; }
        }

        /// <summary>
        /// 返回一个介于两个值之间的随机数。
        /// </summary>
        /// <param name="min">最小值</param>
        /// <param name="max">最大值</param>
        /// <returns>介于最小值和最大值之间的随机数</returns>
        public static float RandomBetween(float min, float max)
        {
            return min + (float)random.NextDouble() * (max - min);
        }

        #region 扩展
        /// <summary>
        /// 将SceneNodeOrdering枚举转换为一个int值
        /// </summary>
        /// <param name="so">节点顺序</param>
        /// <returns></returns>
        public static int GetValue(this SceneNodeOrdering so)
        {
            return (int)so;
        }
        
        /// <summary>
        /// 通过截取w分量将一个Vector4转换为一个Vector3。
        /// </summary>
        /// <param name="v4"></param>
        /// <returns></returns>
        public static Vector3 ToVector3(this Vector4 v4)
        {
            Vector3 v3 = new Vector3(v4.X, v4.Y, v4.Z);
            return v3;
        }
        #endregion
    }
}
